package com.lx.constant.others.options;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.TypeReference;
import com.lx.inter.query.WriteCacheCallBack;
import lombok.Getter;
import lombok.ToString;

import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 配合Lambda表达式进行相关缓存入项的基础缓存配置参数,详细请看相关实现类
 *
 * @author lx
 * @create 2023/1/31 14:15
 **/
@Getter
@ToString
public abstract class CacheOption implements Serializable {
    /**
     * apache 日志打印输出
     */
    protected final Log log = LogFactory.get(this.getClass());

    /**
     * 缓存通用前缀
     */
    protected final String LOCK_PREFIX = "lambda_query_lock:";

    /**
     * 缓存异步处理执行器
     */
    protected static final ThreadPoolExecutor CACHE_EXECUTOR;

    static {
        int avp = Runtime.getRuntime().availableProcessors();
        //初始化线程池
        CACHE_EXECUTOR = new ThreadPoolExecutor(avp, avp << 2, 30, TimeUnit.SECONDS, new LinkedBlockingQueue(100000), new ThreadPoolExecutor.CallerRunsPolicy());
    }

    /**
     * @param cacheName 缓存名称
     */
    protected CacheOption(String cacheName) {
        this.cacheName = cacheName;
    }

    /**
     * 缓存转换类,如存在缓存,则可以提供该类用户转换
     */
    private Type cacheConvertClass;

    /**
     * 缓存名称
     */
    private String cacheName;

    /**
     * 缓存有效时长
     */
    private long timeOut = 300;

    /**
     * 缓存有效时长单位
     */
    private TimeUnit timeUnit = TimeUnit.SECONDS;


    public <ChildOption extends CacheOption> ChildOption setCacheConvertClass(Class<?> cacheConvertClass) {
        this.cacheConvertClass = cacheConvertClass;
        return (ChildOption) this;
    }

    /**
     * 缓存转换类类型,同 cacheConvertClass 属性效果一致 ,此类用于缓存结果存在多泛型条件下的转换
     *
     * @param cacheConvertTypeReference
     * @param <ChildOption>
     * @return
     */
    public <ChildOption extends CacheOption> ChildOption setCacheConvertTypeReference(TypeReference<?> cacheConvertTypeReference) {
        this.cacheConvertClass = cacheConvertTypeReference.getType();
        return (ChildOption) this;
    }

    public <ChildOption extends CacheOption> ChildOption setCacheName(String cacheName) {
        this.cacheName = cacheName;
        return (ChildOption) this;
    }

    public <ChildOption extends CacheOption> ChildOption setTimeOut(long timeOut) {
        this.timeOut = timeOut;
        return (ChildOption) this;
    }

    public <ChildOption extends CacheOption> ChildOption setTimeUnit(TimeUnit timeUnit) {
        this.timeUnit = timeUnit;
        return (ChildOption) this;
    }

    /**
     * 校验和初始化使用缓存的必须参数
     */
    protected void checkAndInitOptions() {
        if (getCacheConvertClass() == null) {
            throw new NullPointerException("调用缓存查询器时,缓存结果类型不能为空!");
        }
        if (StrUtil.isEmpty(getCacheName())) {
            throw new NullPointerException("调用缓存查询器时,缓存对应的内部名称不能为空!");
        }
        //设置初始值,防止空值异常.
        this.setTimeUnit(getTimeUnit() == null ? TimeUnit.SECONDS : getTimeUnit());
    }

    /**
     * 读取或写入目标数据到缓存种抽象方法,由具体类型实现
     *
     * @param writeCacheCallBack
     * @param <T>
     * @return
     */
    public abstract <T> T handlerByCache(WriteCacheCallBack writeCacheCallBack);

    /**
     * 通用数据查询连接缓存点,如不命中缓存则返回中的数据,适用于外部直接调用
     *
     * @param writeCacheCallBack 写缓存获取目标数据方法
     * @param resPos             如缓存项存在多个,则返回第几个缓存的值,默认取第一个CacheOptions中的值
     * @param cacheOptions
     * @param <Response>
     * @return
     */
    public static <Response> Response readOrWriteQueryCacheByCommon(WriteCacheCallBack writeCacheCallBack, int resPos, CacheOption... cacheOptions) {
        if (ArrayUtil.isEmpty(cacheOptions)) {
            //不进行缓存命中查询,仅返回数据库查询结果
            return (Response) writeCacheCallBack.getTargetData();
        }
        //缓存命中查询,判断需要返回那个缓存项的数据，其他缓存项均走异步加载处理
        int cacheLength = cacheOptions.length;
        //规范获取缓存返回值的结果下标
        resPos = resPos < 0 || resPos > cacheLength - 1 ? 0 : resPos;

        Response resPosRes = null;
        for (int step = 0; step < cacheLength; step++) {
            final CacheOption cacheOption = cacheOptions[step];
            //校验缓存所需的必须参数
            cacheOption.checkAndInitOptions();

            if (resPos != step) {
                //异步加载缓存.（为什么要动态生成事务管理器的原因）
                CACHE_EXECUTOR.execute(() -> cacheOption.handlerByCache(writeCacheCallBack));
                continue;
            }
            //同步加载缓存
            resPosRes = cacheOption.handlerByCache(writeCacheCallBack);
        }
        return resPosRes;
    }


}
